
#include "device/dev_hotplug.h"


namespace iecas_ns
{
    shared_ptr<CDevHotplug> CDevHotplug::sm_intancePtr = nullptr;

    CDevHotplug::CDevHotplug()
    {
        m_commonPtr = shared_ptr<CCommon>(CCommon::getInstance());
        m_jsonUsrPtr = shared_ptr<CJsonUsr>(CJsonUsr::getInstance());
        m_devCommonPtr = shared_ptr<CDevCommon>(CDevCommon::getInstance());
        m_hotplug_sock = -1;
        serial_last_t = 0;
        disk_last_t = 0;
        keyboard_last_t = 0;
        camera_last_t = 0;
        net_last_t = 0;
        sdcard_last_t = 0;
    }

	shared_ptr<CDevHotplug> CDevHotplug::getInstance()
	{
		if(sm_intancePtr == nullptr)
		{
			sm_intancePtr = shared_ptr<CDevHotplug>(new CDevHotplug());
		}
		return sm_intancePtr;
	}

    bool CDevHotplug::init(void)
    {
        const int i_buffersize = 1024;
        int i_ret = 0;
    
        struct sockaddr_nl saddr_nl;
        bzero( &saddr_nl, sizeof( struct sockaddr_nl ) );
        saddr_nl.nl_family = AF_NETLINK;
        saddr_nl.nl_pid = getpid();
        saddr_nl.nl_groups = 1;
    
        int i_sock = socket( PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT );
        if ( -1 == i_sock )
        {
            cout << "hotplug socket error" << endl;
            PLOGD << "hotplug socket error";
            return false;;
        }
        setsockopt( i_sock, SOL_SOCKET, SO_RCVBUF, &i_buffersize, sizeof( i_buffersize ) );

        i_ret = bind( i_sock, ( struct sockaddr * )&saddr_nl, sizeof( struct sockaddr_nl ) );
        if (i_ret < 0)
        {
            cout << "hotplug bind error" << endl;
            PLOGD << "hotplug bind error";
            close(i_sock);
            return false;
        }

        m_hotplug_sock = i_sock;

        m_jsonUsrPtr->getHuaweiAosIp(aos_ip);

        return true;
    }

    void CDevHotplug::start(void)
    {
        thread tempThread(&CDevHotplug::loop, this);
        tempThread.detach();
    }

    void CDevHotplug::loop(void)
    {
        int i_rcvlen = 0;

        while(true)
        {
            /* Netlink message buffer */
            char psz_buf[UEVENT_BUFFER_SIZE * 2] = {0};
            string ss_buf, sub_buf;
            i_rcvlen = recv(m_hotplug_sock, &psz_buf, sizeof(psz_buf), 0);
            if ( i_rcvlen > 0 )
            {
                ss_buf = psz_buf;
                int size = ss_buf.size();

                hotplugJudge(ss_buf);

                // cout << "recv msg: " << ss_buf << endl;
                #ifdef DEBUG
                    PLOGD << "recv msg: " << ss_buf;
                #endif
            }
        }
    }

    void CDevHotplug::hotplugJudge(string sock_s)
    {
        string dev_s;
        int devName, connDir;

        devName = deviceJudge(sock_s, dev_s);
        connDir = connectJudge(sock_s);
        switch(devName)
        {
            case Usb_Serial:
                usb_serial_handle(dev_s, connDir); 
                break;
            case Usb_Disk:
                usb_disk_handle(dev_s, connDir); 
                break;
            case Usb_Keyboard:
                usb_keyboard_handle(dev_s, connDir); 
                break;
            case Usb_Camera:
                usb_camera_handle(dev_s, connDir);
                break;
            case Usb_Net:
                usb_net_handle(dev_s, connDir);
                break;
            case SDcard:
                sdcard_handle(dev_s,connDir);
                break;
            default : 
                break;
        }
    }

    int CDevHotplug::deviceJudge(string sock_s, string &dev_s)
    {
        int fb = Usb_Unknown_Device, size_s;
        string tmp, tmp1;
        size_s = sock_s.size();

        int tty_res = sock_s.find("/tty/ttyUSB");
        tmp = "/tty";
        if(tty_res != -1 && ((tty_res + tmp.size()) < size_s))
        {
            dev_s = "/dev" + sock_s.substr(tty_res + tmp.size(), size_s);
            //std::cout << dev_s << std::endl;
            return Usb_Serial;
        } 

        int udisk_res = sock_s.rfind("/sd");
        tmp = "/sd ";
        if((udisk_res != -1) && (udisk_res < size_s))
        {
            dev_s = sock_s.substr(udisk_res, size_s);
            if(dev_s.size() > tmp.size())
            {
                dev_s = "/dev" + dev_s;
                //std::cout << dev_s << std::endl;
                return Usb_Disk;
            }
        }

        int keyboard_res = sock_s.find("/event");
        if((keyboard_res != -1) && (keyboard_res < size_s))
        {
            dev_s = "/dev/input" + sock_s.substr(keyboard_res, size_s);
            //std::cout << dev_s << std::endl;
            return Usb_Keyboard;
        }

        int camera_res = sock_s.find("linux/");
        tmp = "linux/";
        if((camera_res != -1) && ((camera_res + tmp.size()) < size_s))
        {
            dev_s = "/dev/" + sock_s.substr(camera_res + tmp.size(), size_s);
            //std::cout << dev_s << std::endl;
            return Usb_Camera;
        }

        int net_res = sock_s.rfind("/net/");
        tmp = "/net/";
        if((net_res != -1) && ((net_res + tmp.size()) < size_s))
        {
            dev_s = sock_s.substr(net_res + tmp.size(), size_s);
            tmp1 = "/queues/rx-0";
            if(dev_s.size() < tmp1.size())
            {
                //std::cout << dev_s << std::endl;
                return Usb_Net;
            }
        }

        int sdcard_res = sock_s.rfind("/mmcblk2p1");
        tmp = "/mmcblk2 ";
        if((sdcard_res != -1) && (sdcard_res < size_s))
        {
            dev_s = sock_s.substr(sdcard_res, size_s);
            //cout <<"sdcard_res = "<< sdcard_res << std ::endl;
            //cout <<"dev_s = "<< dev_s << std ::endl;
            if(dev_s.size() > tmp.size())
            {
                dev_s = "/dev" + dev_s;
                //std::cout << dev_s << std::endl;
                return SDcard;
            }
        }

        return fb;
    }

    int CDevHotplug::connectJudge(string sock_s)
    {
        int fb = Usb_Unknown_Action;

        if(sock_s.substr(0, 4) == "add@")
        {
            return Usb_Add;
        }
        if(sock_s.substr(0, 7) == "remove@")
        {
            return Usb_Remove;
        }

        return fb;
    }

    void CDevHotplug::usb_serial_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            cout << "++++++++++ Add usb serial " << dev_s << " ++++++++++" << endl;
            PLOGD << "++++++++++ Add usb serial " << dev_s << " ++++++++++";
            m_devCommonPtr->init_serial(dev_s);
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove usb serial " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove usb serial " << dev_s << " ----------";
            m_devCommonPtr->deinit_serial(dev_s);
        }
    }

    void CDevHotplug::usb_disk_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            cout << "++++++++++ Add usb disk " << dev_s << " ++++++++++" << endl;
            PLOGD << "++++++++++ Add usb disk " << dev_s << " ++++++++++";
            m_devCommonPtr->init_usb_disk(dev_s);
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove usb disk " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove usb disk " << dev_s << " ----------";
            m_devCommonPtr->deinit_usb_disk(dev_s);
        }
    }
    
    void CDevHotplug::usb_keyboard_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            int64_t now_t = m_commonPtr->getMsTimeStamp();
            if(now_t - keyboard_last_t > 100)
            {
                cout << now_t << " ++++++++++ Add usb keyboard " << dev_s << " ++++++++++" << endl;
                PLOGD << now_t << " ++++++++++ Add usb keyboard " << dev_s << " ++++++++++";
                keyboard_last_t = now_t;
                m_devCommonPtr->init_usb_keyboard(dev_s);
            }
            else
            {
                cout << now_t << " ++++++++++ Drop usb keyboard " << dev_s << " ++++++++++" << endl;
                PLOGD << now_t << " ++++++++++ Drop usb keyboard " << dev_s << " ++++++++++";
            }
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove usb keyboard " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove usb keyboard " << dev_s << " ----------";
            m_devCommonPtr->deinit_usb_keyboard(dev_s);
        }
    }

    void CDevHotplug::usb_camera_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            int64_t now_t = m_commonPtr->getMsTimeStamp();
            if(now_t - camera_last_t > 10)
            {
                cout << now_t << " ++++++++++ Add usb camera " << dev_s << " ++++++++++" << endl;
                PLOGD << now_t << " ++++++++++ Add usb camera " << dev_s << " ++++++++++";
                camera_last_t = now_t;
                m_devCommonPtr->init_usb_camera(dev_s);
            }
            else
            {
                cout << now_t << " ++++++++++ Drop usb camera " << dev_s << " ++++++++++" << endl;
                PLOGD << now_t << " ++++++++++ Drop usb camera " << dev_s << " ++++++++++";
            }
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove usb camera " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove usb camera " << dev_s << " ----------";
            m_devCommonPtr->deinit_usb_camera(dev_s);
        }
    }

    void CDevHotplug::usb_net_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            cout << "++++++++++ Add usb net " << dev_s << " ++++++++++" << endl;
            PLOGD << "++++++++++ Add usb net " << dev_s << " ++++++++++";
            m_devCommonPtr->init_usb_net(dev_s);
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove usb net " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove usb net " << dev_s << " ----------";
            m_devCommonPtr->deinit_usb_net(dev_s);
        }
    }

    void CDevHotplug::sdcard_handle(string dev_s, int connDir)
    {
        if(connDir == Usb_Add)
        {
            cout << "++++++++++ Add sdcard " << dev_s << " ++++++++++" << endl;
            PLOGD << "++++++++++ Add sdcard " << dev_s << " ++++++++++";
            m_devCommonPtr->init_sdcard(dev_s);
        }
        else if(connDir == Usb_Remove)
        {
            cout << "---------- Remove sdcard " << dev_s << " ----------" << endl;
            PLOGD << "---------- Remove sdcard " << dev_s << " ----------";
            m_devCommonPtr->deinit_sdcard(dev_s);
        }
    }
}
